﻿using System.Collections.Generic;

using UnityEngine;
using UnityEngine.Pool;
using UnityEngine.UI;

/// <summary>
///   <para>Adds an outline to a graphic using IVertexModifier.</para>
/// </summary>
[AddComponentMenu("UGUI/Effects/Shadow")]
[RequireComponent(typeof(Text))]    // 只能为Text使用!!!
public class UGUIShadow : BaseMeshEffect
{
    // 因为顶点的渲染顺序是固定的, 所以写一个模板类, 避免多次计算
    private static List<int> IndicesTemplate = new List<int>();

    private const float kMaxEffectDistance = 600f;

    [SerializeField]
    private Color effectColor = new Color(0f, 0f, 0f, 1f);

    private Color saveEffectColor;
    bool m_IsGray;

    [SerializeField]
    private Vector2 effectDistance = new Vector2(2f, -2f);

    [SerializeField]
    private bool useGraphicAlpha = true;

    protected override void Awake()
    {
        base.Awake();
    }

    /// <summary>
    ///   <para>Color for the effect.</para>
    /// </summary>
    public Color EffectColor
    {
        get
        {
            return this.EffectColor1;
        }
        set
        {
            this.EffectColor1 = value;
            if (base.graphic != null)
            {
                base.graphic.SetVerticesDirty();
            }
        }
    }

    /// <summary>
    ///   <para>How far is the shadow from the graphic.</para>
    /// </summary>
    public Vector2 EffectDistance
    {
        get
        {
            return this.EffectDistance1;
        }
        set
        {
            if (value.x > 600f)
            {
                value.x = 600f;
            }
            if (value.x < -600f)
            {
                value.x = -600f;
            }
            if (value.y > 600f)
            {
                value.y = 600f;
            }
            if (value.y < -600f)
            {
                value.y = -600f;
            }

            if (this.EffectDistance1 == value)
            {
                return;
            }

            this.EffectDistance1 = value;

            if (base.graphic != null)
            {
                base.graphic.SetVerticesDirty();
            }
        }
    }

    /// <summary>
    ///   <para>Should the shadow inherit the alpha from the graphic?</para>
    /// </summary>
    public bool UseGraphicAlpha
    {
        get
        {
            return this.useGraphicAlpha;
        }
        set
        {
            this.useGraphicAlpha = value;
            if (base.graphic != null)
            {
                base.graphic.SetVerticesDirty();
            }
        }
    }

    public Color EffectColor1 { get => effectColor; set => effectColor = value; }
    public Vector2 EffectDistance1 { get => effectDistance; set => effectDistance = value; }

    protected UGUIShadow()
    {
    }

    //protected void OnValidate()
    //{
    //    this.EffectDistance = this.effectDistance;
    //    //base.OnValidate();
    //}

    protected void ApplyShadowZeroAlloc(List<UIVertex> verts, Color32 color, int start, int end, float x, float y)
    {
        int num = verts.Count + end - start;
        if (verts.Capacity < num)
        {
            verts.Capacity = num;
        }
        for (int i = start; i < end; i++)
        {
            UIVertex uIVertex = verts[i];
            verts.Add(uIVertex);

            Vector3 position = uIVertex.position;
            position.x += x;
            position.y += y;
            uIVertex.position = position;

            Color32 color2 = color;
            if (this.useGraphicAlpha)
            {
                color2.a = (byte)(color2.a * verts[i].color.a / 255);
            }
            uIVertex.color = color2;

            verts[i] = uIVertex;
        }
    }

    protected void ApplyShadow(List<UIVertex> verts, Color32 color, int start, int end, float x, float y)
    {
        this.ApplyShadowZeroAlloc(verts, color, start, end, x, y);
    }

    public override void ModifyMesh(VertexHelper vh)
    {
        if (!this.IsActive()) { return; }

        List<UIVertex> list = ListPool<UIVertex>.Get();
        vh.GetUIVertexStream(list);
        CutUIVertexList(list);

        this.ApplyShadow(list, this.EffectColor, 0, list.Count, this.EffectDistance1.x, this.EffectDistance1.y);

        vh.Clear();
        List<int> indices = GetIndices(list);
        vh.AddUIVertexStream(list, indices);

        ListPool<UIVertex>.Release(list);
    }

    // 获取对应的顶点顺序
    protected List<int> GetIndices(List<UIVertex> list)
    {
        int groupCount = list.Count / 4;
        int count = groupCount * 6;
        if (IndicesTemplate.Capacity < count)
        {
            IndicesTemplate.Capacity = count;

            int start = IndicesTemplate.Count / 6;
            int vstart;
            for (int i = start; i < groupCount; i++)
            {
                vstart = 4 * i;
                IndicesTemplate.Add(vstart);
                IndicesTemplate.Add(vstart + 1);
                IndicesTemplate.Add(vstart + 2);
                IndicesTemplate.Add(vstart + 2);
                IndicesTemplate.Add(vstart + 3);
                IndicesTemplate.Add(vstart);
            }
        }
        return IndicesTemplate.GetRange(0, count);
    }

    // 一个四边形的顶点顺序为 0, 1, 2, 2, 3, 0, 因此 index=3, index=5 是重复的, 可以裁剪掉
    protected void CutUIVertexList(List<UIVertex> list)
    {
        int count = list.Count;
        for (int i = count - 6; i >= 0; i -= 6)
        {
            list.RemoveAt(i + 5);
            list.RemoveAt(i + 3);
        }
    }

    public void SetGray(bool isGray)
    {
        if (isGray)
            Gray();
        else
            ResetGray();
    }

    public void Gray()
    {
        if (EffectColor1.r == EffectColor1.g && EffectColor1.r == EffectColor1.b)
            return;
        saveEffectColor = EffectColor1;
        // float value = (effectColor.r * 0.22f + effectColor.g * 0.707f + effectColor.b * 0.071f) / 3;
        float value = 101 / 255f;
        EffectColor = new Color(value, value, value, EffectColor1.a);
        m_IsGray = true;
    }

    public void ResetGray()
    {
        if (!m_IsGray)
            return;
        //if (effectColor.r != effectColor.g || effectColor.r != effectColor.b || effectColor.g != effectColor.b)
        //    return;
        EffectColor = saveEffectColor;
        m_IsGray = false;
    }
}